<script setup lang="ts">
import {
  reactive,
  ref,
  onMounted,
  onBeforeMount,
  computed,
  watch,
  watchEffect
} from "vue";
import { FormInfo, InfoContainer } from "../components";
import {
  chartInfo,
  formInfo,
  tableProps,
  barProps
} from "../components/interface";
import ProgressBar from "./components/progressBar.vue";
import { TheStackedLineChart, TheLineChart } from "../components/Chart";
import { AppTable } from "../home/Table";
import { columns } from "./components/columns";
import axios from "axios";
import { rangeArr } from "element-plus";
import { log } from "console";
import { bitUrlApi } from "../../../api/utils";

defineOptions({
  name: "AppRoutine"
});

// const model_class_options = ref(
//   [
//     {
//       value: "工业知识模型",
//       label: "工业知识模型"
//     },
//     {
//       value: "工业规则模型",
//       label: "工业规则模型"
//     }
//   ]
// )

const deploy_domain_options = ref([
  {
    value: "outlier_detection",
    label: "工业异常检测"
  },
  {
    value: "fault_diagnosis",
    label: "工业故障诊断"
  }
]);

const algorithm_dict = ref({
  outlier_detection: [
    { label: "基于区域提议网络的产品质量检测算法", value: "faster_rcnn" },
    { label: "基于多尺度特征图的产品质量检测算法", value: "SSD" },
    { label: "基于样本权重均衡的产品质量检测算法", value: "retina_net" },
    { label: "基于角点特征提取的产品质量检测算法", value: "corner_net" },
    { label: "基于中心坐标预测的产品质量检测算法", value: "center_net" },
    { label: "基于特征检测解耦的产品质量检测算法", value: "yoloX" }
  ],
  fault_diagnosis: [
    { label: "故障停机故障状态监测算法", value: "Guzhang" },
    { label: "断经停机故障状态监测算法", value: "Duanjing" },
    { label: "断纬停机故障状态监测算法", value: "Duanwei" },
    { label: "紧急停机故障状态监测算法", value: "Jinji" },
    { label: "设备离线故障状态监测算法", value: "Lixian" }
  ]
});

const dataset_dict = ref({
  outlier_detection: [
    { label: "平纹", value: "pingwen" },
    { label: "斜纹", value: "xiewen" },
    { label: "缎纹", value: "duanwen" }
  ],
  fault_diagnosis: [
    // { label: "故障数据", value: "guzhang" }
    { label: "喷水织机故障数据", value: "placeholder1" },
    { label: "整经机故障数据", value: "placeholder2" }
  ]
});

// 使用reactive实现数据自动绑定到组件上
const formProps1: formInfo = reactive({
  title: "训练设置",
  btnNames: ["创建实例", undefined, undefined],
  btnDisabled: [false, false, false],
  formInfoList: [
    {
      id: 1,
      title: "模型名称",
      inputType: "2",
      placeholder: "yy-mm-dd-xx",
      options: undefined
    },
    {
      id: 2,
      title: "模型分类",
      inputType: "2",
      placeholder: "工业知识模型",
      options: undefined
      // [
      //   {
      //   value: "工业知识模型",
      //   label: "工业知识模型"
      //   }
      // ]
    },
    {
      id: 3,
      title: "应用领域",
      inputType: "0",
      placeholder: "请选择应用领域",
      options: deploy_domain_options.value
    },
    {
      id: 4,
      title: "算法选择",
      inputType: "0",
      placeholder: "请选择算法",
      options: undefined
    },
    {
      id: 5,
      title: "数据选择",
      inputType: "0",
      placeholder: "请选择数据",
      options: undefined
    }
  ]
});

const num_epoch = ref();
const num_batch = ref();
const placeholder = ref("请选择");
const epoch_options = ref([
  {
    label: 2,
    value: 2
  },
  {
    label: 5,
    value: 5
  },
  {
    label: 10,
    value: 10
  }
]);
const batch_options = ref([
  {
    label: 2,
    value: 2
  },
  {
    label: 16,
    value: 16
  },
  {
    label: 32,
    value: 32
  }
]);

const formInfo1 = ref();

function clickBtn() {
  // model_name: formInfo1.value.infoChildren[0].inputValue
  // model_class: formInfo1.value.infoChildren[1].value
  // deploy_domain: formInfo1.value.infoChildren[2].value
  // algorithm_class: formInfo1.value.infoChildren[3].value
  // dataset_class: formInfo1.value.infoChildren[4].value
  // console.log(formInfo1.value.infoChildren[0].inputValue)
  // console.log(formInfo1.value.infoChildren[1].value)
  // console.log(formInfo1.value.infoChildren[2].value)
  // console.log(formInfo1.value.infoChildren[3].value)
  // console.log(formInfo1.value.infoChildren[4].value)
  // console.log(num_epoch.value)
  // console.log(num_batch.value)
  if (!formInfo1.value.infoChildren[0].inputValue) {
    alert("请输入模型名称！");
    return;
  }
  if (!formInfo1.value.infoChildren[1].value) {
    formInfo1.value.infoChildren[1].value =
      formProps1.formInfoList[1].placeholder;
    // formProps1.formInfoList[1].options[0]["value"];
  }
  if (!formInfo1.value.infoChildren[2].value) {
    alert("请选择应用领域！");
    return;
  }
  if (!formInfo1.value.infoChildren[3].value) {
    alert("请选择算法！");
    return;
  }
  if (!formInfo1.value.infoChildren[4].value) {
    alert("请选择数据！");
    return;
  }
  if (!num_epoch.value) {
    alert("请选择训练轮数！");
    return;
  }
  if (!num_batch.value) {
    alert("请选择批大小！");
    return;
  }
  createInstance();
}

function createInstance() {
  // formInfo2.value.infoChildren[0].value = formInfo1.value.infoChildren[2].value
  console.log("与后端通信，创建实例！");
  // todo: 与真实故障数据对齐
  let tmp_dataset =
    formInfo1.value.infoChildren[2].value == "fault_diagnosis"
      ? "guzhang"
      : formInfo1.value.infoChildren[4].value;

  let tmp_method =
    formInfo1.value.infoChildren[2].value == "outlier_detection"
      ? "yoloX"
      : formInfo1.value.infoChildren[3].value;

  axios
    .post(bitUrlApi("/train/model_create"), {
      model_name: formInfo1.value.infoChildren[0].inputValue,
      // model_class: formInfo1.value.infoChildren[1].value,
      model_class: "knowledge",
      model_apply: formInfo1.value.infoChildren[2].value,
      model_method: tmp_method,
      model_dataset_name: tmp_dataset,
      batch_size: num_batch.value,
      learning_rate: "5e-5",
      epoch: num_epoch.value,
      is_transfer: false,
      is_light: false,
      selected_total_iter: "10000"
    })
    .then(response => {
      // 处理响应
      console.log(response.data);
      if (response.data.data.start == false) {
        model_running.value = true; // a model is training on the backend
        alert("当前存在运行中任务");
        return;
      } else {
        console.log("model_name: ", formInfo1.value.infoChildren[0].inputValue);
        console.log("model_class: ", formInfo1.value.infoChildren[2].value);
        console.log("model_apply: ", formInfo1.value.infoChildren[2].value);
        console.log("model_method: ", formInfo1.value.infoChildren[3].value);
        console.log(
          "model_dataset_name: ",
          formInfo1.value.infoChildren[4].value
        );
        console.log("epoch: ", num_epoch.value);
        console.log("learning_rate: ", "5e-5");
        console.log("batch: ", num_batch.value);
        // //the model is running now!
        model_running.value = true;
        resetChartData();
      }
    })
    .catch(error => {
      // 处理错误
      console.error(error);
    });
}

watchEffect(() => {
  if (!formInfo1.value) {
    return;
  }
  if (formInfo1.value.infoChildren[2].value != undefined) {
    formInfo1.value.infoChildren[3].value = undefined;
    formProps1.formInfoList[3].options =
      algorithm_dict.value[formInfo1.value.infoChildren[2].value];
    formInfo1.value.infoChildren[4].value = undefined;
    formProps1.formInfoList[4].options =
      dataset_dict.value[formInfo1.value.infoChildren[2].value];
    // console.log(`"${formInfo1.value.infoChildren[1].value}"`)
    // console.log(deploy_domain_option_dict[`"${formInfo1.value.infoChildren[1].value}"`])
  }
  //  if (formInfo1.value && formInfo1.value.infoChildren[1].value) {
  //   console.log(formInfo1.value.infoChildren[1].value);
  //  }
});

const formProps2: formInfo = reactive({
  title: "训练管理",
  btnNames: [undefined, "停止", "存储"],
  btnDisabled: [false, false, false],
  formInfoList: [
    // {
    //   id: 1,
    //   title: "应用领域",
    //   inputType: "2",
    //   placeholder: "请创建实例",
    //   options: undefined
    // }
  ]
});

const formInfo2 = ref();

onMounted(() => {
  // 设置为只读输入框
  formInfo1.value.infoChildren[1].readonly = true;
  formInfo1.value.infoChildren[1].inputValue = "工业知识模型";
});

// const chartProps: chartInfo = reactive({
//   title: "训练日志", // 图表名称
//   legend: ["Loss", "Acc"], // 图例名称
//   xAxisName: "Iter", // x轴名称
//   xAxisData: ["20", "80", "120", "160"], // x轴数据设置
//   yAxisSetting: {}, // y轴属性设置
//   seriesData: [
//     {
//       name: "Loss", // 与图例相同
//       data: [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] // 数据点
//     },
//     {
//       name: "Acc", // 与图例相同
//       data: [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] // 数据点
//     }
//   ]
// });

const lossChartProps: chartInfo = reactive({
  title: "",
  legend: ["loss", "xxxx"],
  xAxisName: "迭代轮数",
  xAxisData: ["0"],
  yAxisSetting: {
    name: "损失"
  },
  seriesData: [
    {
      name: "loss",
      data: [0.0]
    },
    {
      name: "xxxx",
      data: []
    }
  ]
});

const accChartProps: chartInfo = reactive({
  title: "",
  legend: ["acc", "xxxx"],
  xAxisName: "迭代轮数",
  xAxisData: ["0"],
  yAxisSetting: {
    name: "准确率"
  },
  seriesData: [
    {
      name: "acc",
      data: [0.0]
    },
    {
      name: "xxxx",
      data: []
    }
  ]
});

const propsTable: tableProps = reactive({
  tableData: [
    {
      model_name: "",
      model_class: "",
      deploy_type: "",
      model_size: "",
      model_performance: ""
    }
  ], // 表格数据
  columns: columns, // 表格样式
  pagination: { pageSize: 2 }, // 分页配置
  onPagination: false // 是否启用分页
});

watch(propsTable.tableData, () => {
  console.log("检测到了变化");
});

// whether the model is running or not.
const model_running = ref(false);
const model_running_name = ref("");

watch(model_running, (newVal, oldVal) => {
  if (!oldVal && newVal) {
    // change from false to true
    // start timers to fetch data
    console.log("模型开始训练");
    // getRunningState()
    barProps1.fetchData = fetchProgress;
    barRef.value.fetching = true;
    startTimer();
  } else if (oldVal && !newVal) {
    // change from false to true
    // stop all timers
    barProps1.fetchData = undefined;
    barRef.value.fetching = false;
    stopTimer();
  }
});

const model_apply_2_chinese = {
  fault_diagnosis: "故障诊断",
  outlier_detection: "异常检测"
};

const getRunningState = async () => {
  try {
    const res = await axios.post(bitUrlApi("/train/model_table_state"));
    model_running_name.value = res.data.data.model_name;
    console.log("model state", res.data.data);
    if (
      res.data.data.model_name &&
      res.data.data.model_class &&
      res.data.data.model_apply &&
      res.data.data.model_size
    ) {
      propsTable.tableData[0] = {
        model_name: res.data.data.model_name,
        model_class: "知识模型",
        deploy_type: model_apply_2_chinese[res.data.data.model_apply],
        model_size: res.data.data.model_size,
        // model_performance:
        //   accChartProps.seriesData[0].data.length > 0
        //     ? accChartProps.seriesData[0].data[
        //         accChartProps.seriesData[0].data.length - 1
        //       ].toFixed(2)
        //     : ""
        model_performance: res.data.data.model_perform
      };
      console.log("propsTable.tableData", propsTable.tableData);
    }
    console.log("propsTable.tableData", propsTable.tableData);

    if (res.data.data.is_train == true) {
      return true;
    } else {
      return false;
    }
  } catch (error) {
    console.log("model table state获取数据时出错:", error);
    return undefined;
  }
};

const stop_training = async () => {
  // if (!model_running.value) {
  //   // there is not any model running
  //   return
  // }
  let running = await getRunningState();
  console.log("running", running);
  if (running == true) {
    await axios
      .post(bitUrlApi("/train/model_stop"))
      .then(res => {
        // 接收
        console.log(res);
        console.log("正在训练的模型已停止！");
        alert("模型已停止训练！");
      })
      .catch(err => {
        alert("请求失败请再次尝试!");
        console.log("请求失败请再次尝试：", err);
      });
  } else {
    console.log("模型已停止，无需重复操作！");
    alert("模型已停止，无需重复操作！");
  }
};

const store_model = async () => {
  await axios
    .post(bitUrlApi("/train/model_save"))
    .then(res => {
      // 接收
      console.log(res);
      if (res.data.data["success"] == false) {
        alert(res.data.data["error"]);
      } else {
        alert("存储成功");
      }
    })
    .catch(err => {
      console.log("请求失败：", err);
    });
};

const barProps1: barProps = reactive({
  title: "训练进度",
  fetchData: undefined // a function return {"progress": number, "training": boolean}
});

const barRef = ref();

const fetchProgress = async () => {
  try {
    const results = await axios.post(bitUrlApi("/train/model_progress"));

    return {
      progress: Number((results.data.data.progress * 100).toFixed(2)),
      // progress: (results.data.data.progress * 100).toPrecision(2),
      training: results.data.data.is_train
    };
  } catch (error) {
    console.error("Failed to fetch data:", error);
    return {
      progress: "NaN",
      training: false
    };
  }
};

const timer = ref(null);

function startTimer() {
  // 当 fetchData 传入数据时，启动定时器，每秒调用一次 handleTimer 方法
  if (timer.value === null) {
    timer.value = setInterval(handleTimer, 1000);
  }
}

function stopTimer() {
  // 停止定时器
  if (timer.value !== null) {
    clearInterval(timer.value);
    timer.value = null;
  }
}

async function handleTimer() {
  // fetch loss, acc, model state
  let running_state = await getRunningState();
  if (running_state == false) {
    // fetch OK, and find the the model is stop
    model_running.value = running_state;
  }
  fetchLoss();
  fetchAcc();
}

async function fetchAcc() {
  try {
    const response = await axios.post(bitUrlApi("/train/model_acc_state"), {});
    // console.log(response.data.data)
    if (response.data.data["acc_data"] == null) {
      console.log("The acc data is not available now.");
      return;
    }
    let split_arr = splitArr(response.data.data["acc_data"]);

    accChartProps.xAxisData.splice(0, lossChartProps.xAxisData.length);
    accChartProps.xAxisData.push(...split_arr["x_arr"]);

    accChartProps.seriesData[0].data.splice(
      0,
      lossChartProps.seriesData[0].data.length
    );
    accChartProps.seriesData[0].data.push(...split_arr["y_arr"]);
  } catch (error) {
    console.log("acc请求失败", error);
  }
}

async function fetchLoss() {
  try {
    const response = await axios.post(bitUrlApi("/train/model_loss_state"), {});
    // console.log(response.data.data)
    // lossChartProps.seriesData = response.data.data
    // lossChartProps.xAxisData = rangeArr(lossChartProps.yAxisData.length)
    if (response.data.data["loss_data"] == null) {
      console.log("The loss data is not available now.");
      return;
    }
    // console.log(response.data.data["loss_data"])
    let split_arr = splitArr(response.data.data["loss_data"]);
    // console.log('split_arr',split_arr);

    lossChartProps.xAxisData.splice(0, lossChartProps.xAxisData.length);
    lossChartProps.xAxisData.push(...split_arr["x_arr"]);

    lossChartProps.seriesData[0].data.splice(
      0,
      lossChartProps.seriesData[0].data.length
    );
    lossChartProps.seriesData[0].data.push(...split_arr["y_arr"]);
    // console.log("lossChartProps", lossChartProps);

    // console.log(split_arr)
    // lossChartProps.xAxisData = split_arr[0]
    // console.log(response.data.data["loss_data"])
    // lossChartProps.seriesData = response.data.data["loss_data"]
  } catch (error) {
    console.log("loss请求失败", error);
  }
}

function resetChartData() {
  // lossChartProps.xAxisData.splice(0, lossChartProps.xAxisData.length);
  // lossChartProps.seriesData[0].data.splice(
  //   0,
  //   lossChartProps.seriesData[0].data.length
  // );
  // accChartProps.xAxisData.splice(0, accChartProps.xAxisData.length);
  // accChartProps.seriesData[0].data.splice(
  //   0,
  //   accChartProps.seriesData[0].data.length
  // );
  lossChartProps.xAxisData = ["0"];
  accChartProps.xAxisData = ["0"];
  lossChartProps.seriesData[0].data = [0.0];
  accChartProps.seriesData[0].data = [0.0];
}

function splitArr(origin_arr) {
  let x_arr = Array<string>();
  let y_arr = Array<number>();
  // console.log(origin_arr)
  for (let data_point of origin_arr) {
    // console.log(data_point)
    x_arr.push(String(data_point[0]));
    y_arr.push(Number(data_point[1]));
  }
  return { x_arr: x_arr, y_arr: y_arr };
}
</script>

<template>
  <div>
    <el-row class="app-routine" :gutter="20">
      <el-col :span="12">
        <FormInfo
          ref="formInfo1"
          :title="formProps1.title"
          :btnNames="formProps1.btnNames"
          :btnDisabled="formProps1.btnDisabled"
          :formInfoList="formProps1.formInfoList"
          @btnClick1="clickBtn"
        >
          <!-- <template #afc0>
          训练设置
          <el-divider></el-divider>
        </template> -->
          <template #afc1>
            <el-divider style="margin-top: 10px; margin-bottom: 10px" />
            <el-row>参数配置</el-row>
            <el-row>
              <el-col :span="4" class="input-title"> 训练轮数 </el-col>
              <el-col :span="5" class="input-info-row">
                <el-select v-model="num_epoch" :placeholder="placeholder">
                  <el-option
                    v-for="item in epoch_options"
                    :key="item.value"
                    :label="item.label"
                    :value="item.value"
                  />
                </el-select>
              </el-col>
              &nbsp; &nbsp; &nbsp;
              <el-col :span="3" class="input-title"> 批大小 </el-col>
              <el-col :span="5" class="input-info-row">
                <el-select v-model="num_batch" :placeholder="placeholder">
                  <el-option
                    v-for="item in batch_options"
                    :key="item.value"
                    :label="item.label"
                    :value="item.value"
                  />
                </el-select>
              </el-col>
            </el-row>
          </template>
        </FormInfo>
      </el-col>
      <el-col :span="12">
        <InfoContainer :title="formProps2.title">
          <el-row>
            {{ model_running_name }}
          </el-row>
          <el-row>
            <el-col :span="5">
              <el-button
                type="warning"
                class="info-btn form-btn2"
                @click="stop_training"
                >停止</el-button
              >
            </el-col>
            <el-col :span="1" />
            <el-col :span="5">
              <el-button
                type="danger"
                class="info-btn form-btn3"
                @click="store_model"
                >存储</el-button
              >
            </el-col>
          </el-row>
          <el-divider style="margin-top: 10px; margin-bottom: 10px" />
          <el-row>
            <el-col>
              <ProgressBar
                ref="barRef"
                :title="barProps1.title"
                :fetch-data="barProps1.fetchData"
              />
            </el-col>
          </el-row>
          <el-divider style="margin-top: 10px; margin-bottom: 10px" />
          <el-row>
            <el-col>
              <TheLineChart
                :class="['the-stacked-line-chart', 'normal-height']"
                :title="lossChartProps.title"
                :legend="lossChartProps.legend"
                :xAxisName="lossChartProps.xAxisName"
                :xAxisData="lossChartProps.xAxisData"
                :yAxisSetting="lossChartProps.yAxisSetting"
                :seriesData="lossChartProps.seriesData"
              />
            </el-col>
          </el-row>
          <el-row>
            <el-col>
              <TheLineChart
                :class="['the-stacked-line-chart', 'normal-height']"
                :title="accChartProps.title"
                :legend="accChartProps.legend"
                :xAxisName="accChartProps.xAxisName"
                :xAxisData="accChartProps.xAxisData"
                :yAxisSetting="accChartProps.yAxisSetting"
                :seriesData="accChartProps.seriesData"
              />
            </el-col>
          </el-row>
          <el-divider style="margin-top: 10px; margin-bottom: 10px" />
          <el-row>
            <el-col>
              <AppTable
                class="add-table"
                :table-data="propsTable.tableData"
                :columns="propsTable.columns"
                :pagination="propsTable.pagination"
                :on-pagination="propsTable.onPagination"
              />
            </el-col>
          </el-row>
        </InfoContainer>
      </el-col>
    </el-row>
  </div>
</template>

<style lang="scss" scoped>
$input-height: 70px;
$input-title-height: 22px;
$input-row-height: $input-height - $input-title-height;
$input-upload-aside-width: 5%;
$input-upload-container-height: 32px;

.app-routine {
  height: 93.5vh;

  .afc1-title {
    font-weight: bold;
  }

  .afc1-content {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;

    .afc1-item {
      flex: 0 0 48%;

      span {
        color: rgb(176 177 179);
      }
    }
  }
}

.the-stacked-line-chart {
  overflow: hidden;
  // width: 100px;
}

.normal-height {
  height: 25vh;
}

.input-title {
  height: $input-title-height;
  margin-bottom: 1vh;
  font-size: 14px;
  font-weight: 400;
  line-height: $input-title-height;
}

.input-info-row {
  height: $input-row-height;
}

.input-upload-container {
  .input-upload-aside {
    width: $input-upload-aside-width;
    overflow: hidden;

    .input-upload-btn {
      width: 100% !important;
      height: $input-upload-container-height;
      line-height: $input-upload-container-height;
      cursor: pointer;
      border-top: 1px solid rgb(220 223 230);
      border-right: 1px solid rgb(220 223 230);
      border-bottom: 1px solid rgb(220 223 230);
      border-top-right-radius: 4px;
      border-bottom-right-radius: 4px;
    }

    .input-upload-btn:hover {
      background-color: rgb(234 234 234);
    }
  }

  .input-upload-main {
    overflow: hidden;
  }
}

.info-btn {
  width: 100% !important;
  margin-top: 2vh;
}

.form_btn1 {
  background-color: #0c78e1;
}

.form_btn2 {
  background-color: #e6a23c;
}

.form_btn3 {
  background-color: #c34d4d;
}
</style>
